package network;

import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;
import messages.*;
import modelcontroller.ChatController;
import modelcontroller.ChatModel;

/**
 *
 * @author Mariam
 */
public class ChatNI implements Observer {
    
    /**
     * {@value #COMMON_PORT_UDP}
     */
    protected static final int COMMON_PORT_UDP = 21000;
    /**
     * {@value #COMMON_PORT_TCP}
     */
    protected static final int COMMON_PORT_TCP = 22000;
    //protected static final int COMMON_PORT_UDP2 = 21001;
    //protected static final int COMMON_PORT_TCP2 = 22001;
    /**
     * {@value #ADDR_BROADCAST}
     */
    protected static final String ADDR_BROADCAST = "255.255.255.255";
    
    private DatagramSocket socketSendUDP;
    private DatagramSocket socketReceiveUDP;
    private Socket socketSendTCP;
    private ServerSocket socketReceiveTCP;
    protected ChatModel model;
    protected ChatController controller;
    private ThreadSendMsg threadSendMsg;
    private ThreadReceiveMsg threadReceiveMsg;
    private ThreadSendFile threadSendFile;
    private ThreadReceiveFile threadReceiveFile;

    public ChatNI(ChatModel model) {
        try {
            this.model = model;
            this.socketSendUDP = new DatagramSocket();
            this.socketReceiveUDP = new DatagramSocket(COMMON_PORT_UDP);
            this.socketSendTCP = new Socket();
            this.socketReceiveTCP = new ServerSocket(COMMON_PORT_TCP);
        } catch (SocketException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void setController(ChatController controller) {
        this.controller = controller;
    }

    public void sendHello(String username) {
        try {
            System.out.println("Sending of a HELLO message");
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(ADDR_BROADCAST), MessagesFactory.getHello(username, true));//InetAddress.getLocalHost().getHostName(), true));
            threadSendMsg.start();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendHelloAck(String remoteusername) {
        try {
            System.out.println("Sending of a HELLO ACK message");
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(remoteusername), MessagesFactory.getHello(model.getLocalUser().getUsername(), false));//InetAddress.getLocalHost().getHostName(), true));
            threadSendMsg.start();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendBye() {
        try {
            System.out.println("Sending of a BYE message");
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(ADDR_BROADCAST), MessagesFactory.getBye(model.getLocalUser().getUsername()));
            threadSendMsg.start();
            controller.performByeSent();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void sendMsg(String username, String msgToSend) {
        try {
            String hostname = model.getHashmapUsers().get(username).getHostname();
            System.out.println("Sending of a CHAT message");
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(hostname), MessagesFactory.getChatMessage(model.getLocalUser().getUsername(), msgToSend));
            threadSendMsg.start();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendFileRequest(String username, String filename, long size) {
        System.out.println("Sending of a FILE NOTIF message");
        try {
            String hostname = model.getHashmapUsers().get(username).getHostname();
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(hostname), MessagesFactory.getFileNotif(model.getLocalUser().getUsername(), filename, true, false, size));
            threadSendMsg.start();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendFileAnswer(String username, String filename, boolean choice, long size) {
        System.out.println("Sending of a FILE ANSWER message : " + choice);
        try {
            String hostname = model.getHashmapUsers().get(username).getHostname();
            threadSendMsg = new ThreadSendMsg(socketSendUDP, InetAddress.getByName(hostname), MessagesFactory.getFileNotif(model.getLocalUser().getUsername(), filename, false, choice, size));
            threadSendMsg.start();
        } catch (MessageCreationException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendFile(String username, String filepath) {
        try {
            File file = new File(filepath);
            String hostname = model.getHashmapUsers().get(username).getHostname();
            threadSendFile = new ThreadSendFile(socketSendTCP, InetAddress.getByName(hostname), file);
            threadSendFile.start();
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void receive() {
        threadReceiveMsg = new ThreadReceiveMsg(socketReceiveUDP, this);
        threadReceiveMsg.start();
    }

    public void helloRcvd(String username, String hostname) {
        controller.performHelloRcvd(username, hostname);
    }

    public void byeRcvd(String username, String hostname) {
        controller.performByeRcvd(model.getHashmapUsers().get(username));
    }

    public void msgRcvd(String username, String hostname, String msg) {
        controller.performMsgRcvd(model.getHashmapUsers().get(username), msg);
    }

    public void fileRequestRcvd(String username, String filename, long size) {
        controller.performFileRequestRcvd(username, filename, size);
    }

    public void fileAnswerRcvd(String username, String filename, boolean accepted) {
        controller.performFileAnswerRcvd(username, filename, accepted);
    }

    public void receiveFile(String filename, String filepath) {
        try {
            File file = new File(filepath + File.separatorChar + filename);
            file.createNewFile();
            threadReceiveFile = new ThreadReceiveFile(socketReceiveTCP, this, file);
            threadReceiveFile.start();
        } catch (IOException ex) {
            Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void update(Observable o, Object arg) {
        // System.out.println("UPDATE");
        if (arg.equals(ChatModel.Val.STATE)) {
            // we send HELLO
            if (model.getLocalUserIsConnected()) {
                receive();
                sendHello(model.getLocalUser().getUsername());
            } else { // we send BYE
                getThreadReceiveMsg().kill();
                sendBye();
            }
        }
    }

    public ThreadSendMsg getThreadSendMsg() {
        return threadSendMsg;
    }

    public ThreadReceiveMsg getThreadReceiveMsg() {
        return threadReceiveMsg;
    }
}
